# 模拟M300仪器，用udp广播数据
# 参考数据 raw_data\课题5\M300\asc20190924\M300.udp.1029.dat
# 自动变化数据：
# 格式参考 raw_data\课题5\M300\asc20190924\udpout.asc

import parse
import fire
import time
import datetime
import os
import random
import scanf
import regex as re
from math import sin
import socket
from sys import platform
# 客户端
from thingsboard_gateway.tb_client.tb_device_mqtt import TBDeviceMqttClient

# 输出log，便于调试
import logging

def get_config(config_file = r'raw_data/课题5/M300/asc20190924/udpout.asc'):
    """从配置文件生成相关格式配置"""
    @parse.with_pattern(r'\"([\w\/\^\(\)\ \.\,%]+)\"')
    def parse_quoted_str(text):
        return text[1:-1]

    display_names = []
    format_strs = []
    col_names = []
    data_types = []
    with open(config_file,mode='r') as config_file:
        for line in config_file.readlines():
            if line.startswith(';'): continue
            line = line.strip()
            if line.strip()=="": continue
            format_str = r'{display_name:quoted} {tag1:w} {tag2:d} {tag3:w} {format:quoted}'
            #print(line)
            res = parse.parse(format_str, line,dict(quoted=parse_quoted_str))
            print(res)
            display_names.append(res.named['display_name'])
            format = res.named['format']
            format_strs.append(format)
            col_names.append(re.sub(r'[^a-zA-Z0-9]','_',res.named['display_name']).strip().lower())
            f = re.findall(r'\%.*?([sfcdge])',format)[0]
            data_types.append(f)
        data_types = dict(zip(col_names,data_types))
    return display_names,format_strs,col_names,data_types

def main(host='test2.bj-jiashi.com',
        port=10006,
        token='M300', 
        udp_port = 1029,
        config_file = r'raw_data\课题5\M300\asc20190924\udpout.asc',
        max_time_hours = 24,
        mode='sin',
        log_level = 'INFO'):
    """UDP接收上传
    """
    # 初始化logger
    logging.basicConfig(level = getattr(logging,log_level.upper(),logging.INFO)) # 默认info
    file_base_name = os.path.basename(__file__)
    logger = logging.getLogger(file_base_name)

    # 分隔符
    sep = ''
    # 列名  
    display_names,format_strs,col_names,data_types = get_config(config_file)

    # 默认值，为保持完整性
    defaults_str = 'M300,2021/01/24,03:58:08,    0,116.31483, 40.15115,       33,  3.1,   0.0,  87.9,    1.260,      0.0,    270.0,      0.0,39960,  6.5,  0.0,   0.0000,   0.0000,     0.00,     0.00,     0.00,    0.122,    6.098,   -0.020,    0.226,1.54e+009,ENDM300'
    
    # 生成格式化字符串
    # 采用parse格式
    
    datetime_formats = '4d,2d,2d,2d,2d,2d'.split(',')
    datetime_cols = 'yyyy,MM,dd,hh,mm,ss'.split(',')
    datetime_parse_formats = r'{yyyy:4d}/{MM:2d}/{dd:2d},{hh:2d}:{mm:2d}:{ss:2d},'

    read_format_str = 'M300,'+ datetime_parse_formats + ','.join( [f'{{{k}:{data_types[k]}}}' for k in col_names[2:-2]]) +',ENDM300\r\n'
    logger.info(read_format_str)
    
    # 建立socket
    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
    if platform != "win32":
        client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
    client.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

    client.bind(("",udp_port))

    # 建立设备连接
    TBD = TBDeviceMqttClient(host, port, token)
    TBD.connect()
    logger.info('mqtt server connected!')

    #data lines
    start_time = time.time()
    while(time.time()<start_time + max_time_hours*24*3600): # max working hours
        data, addr = client.recvfrom(1024)
        message = data.decode('utf-8')
        logger.info(message)

        # 消息解析
        d = message.strip().split(',')[1:-1] # ignore \r \n M300 and ENDM300

        ## 时间处理
        datetime_str = d[0]+' '+d[1]
        time_time_struct  = time.strptime(datetime_str,'%Y/%m/%d %H:%M:%S')
        time_ts = time.mktime(time_time_struct)

        data_dict = {'ts':-1,'values':{}}

        data_dict['ts'] = int(time_ts*1000)

        ## 数值解析
        dd = {}
        for col_name,v_str in zip(col_names[3:-2],d[3:]): # ignore `seconds_midnight`
            if data_types[col_name].lower() in 'feg':
                dd[col_name] = float(v_str)
            elif data_types[col_name].lower() in 'd':
                dd[col_name] = int(v_str)
            else:
                raise RuntimeError(f'{v_str} is neither integer nor float')
        data_dict['values'] = dd
        logger.info(data_dict)
        
        # 上传
        TBD.send_telemetry(data_dict)

if __name__ == "__main__":
    fire.Fire(main) # 利用fire命令行化
